import { PolarProjectionUtils } from "../utils/polarProjection";
import { LocationPoint, LatLngRect, DetectionPointWithApiFields } from "../types/earth";
import { rectIntersects, pointIntersectsRect } from "../utils/earthUtils";

// 选区状态
interface SelectionState {
  isSelecting: boolean;
  clickCount: number;
  start: { lat: number; lng: number } | null;
  end: { lat: number; lng: number } | null;
}

// 事件回调类型
interface EarthPolarRendererCallbacks {
  tileUrlTemplate?: string; // 瓦片URL模板（已包含planet_id）
  maxZoom?: number; // 最大缩放级别
  onCoordinateClick?: (coord: { lat: number; lng: number } | null) => void;
  onCoordinateHover?: (coord: { lat: number; lng: number } | null) => void;
  onLocationClick?: (location: LocationPoint | null) => void;
  onLocationHover?: (location: LocationPoint | null) => void;
  onDetectionPointClick?: (detectionPoint: DetectionPointWithApiFields | null) => void;
  onDetectionPointHover?: (detectionPoint: DetectionPointWithApiFields | null) => void;
  onRectClick?: (rect: LatLngRect | null) => void;
  onRectHover?: (rect: LatLngRect | null) => void;
  onPatrolAreaClick?: (patrolArea: LatLngRect | null) => void;
  onPatrolAreaHover?: (patrolArea: LatLngRect | null) => void;
  onSelectionChange?: (selectionRect: LatLngRect | null) => void;
  onSelectionEnd?: (
    selectionRect: LatLngRect | null,
    intersectingRects: LatLngRect[],
    intersectingDetectionPoints: DetectionPointWithApiFields[]
  ) => void;
}

export class EarthPolarRenderer {
  private canvas: HTMLCanvasElement;
  private ctx: CanvasRenderingContext2D;
  private image: HTMLImageElement | null = null;
  private imageLoaded = false;
  private tileUrlTemplate?: string;
  private maxZoom: number = 5;
  private tileCache: Map<string, HTMLImageElement> = new Map();
  private failedTileCache: Set<string> = new Set();

  // 极地模式专用的经度范围限制函数
  // 以起始点为基准，确保选区经度不超过180度
  private getPolarLngRange(startLng: number, endLng: number): { minLng: number; maxLng: number } {
    // 归一化经度到-180到180范围
    const normalizeLng = (lng: number) => {
      return ((((lng + 180) % 360) + 360) % 360) - 180;
    };

    const n1 = normalizeLng(startLng);
    const n2 = normalizeLng(endLng);

    // 以起始点为基准，计算到结束点的最短路径
    const diff = n2 - n1;

    // 如果差值超过180度，选择反向路径（跨越-180/180度）
    if (Math.abs(diff) > 180) {
      if (diff > 0) {
        // n2 > n1 且差值 > 180，选择从n1向左到n2的路径
        // 确保范围不超过180度，使用跨越-180/180度的表示
        return {
          minLng: n2,
          maxLng: n1 + 360,
        };
      } else {
        // n2 < n1 且差值 < -180，选择从n1向右到n2的路径
        // 确保范围不超过180度，使用跨越-180/180度的表示
        return {
          minLng: n1,
          maxLng: n2 + 360,
        };
      }
    } else {
      // 差值不超过180度，使用正常范围
      return {
        minLng: Math.min(n1, n2),
        maxLng: Math.max(n1, n2),
      };
    }
  }

  // 计算当前缩放级别
  private getZoomLevel(): number {
    // 基于缩放后的有效显示宽度计算缩放级别
    // 1024宽度 → level 1
    // 2048宽度 → level 2
    // 4096宽度 → level 3
    // 以此类推
    const baseWidth = 1024; // 基础宽度
    const effectiveWidth = baseWidth * this.scale; // 缩放后的有效宽度
    const calculatedLevel = Math.floor(Math.log2(effectiveWidth / baseWidth)) + 1;
    const result = Math.min(Math.max(calculatedLevel, 1), this.maxZoom);

    return result;
  }

  // 获取可见瓦片
  private getVisibleTiles(level: number): { z: number; row: number; col: number; key: string }[] {
    const tiles: { z: number; row: number; col: number; key: string }[] = [];

    if (level === 1) {
      // 1级只显示1_1_1（level_row_col格式）
      tiles.push({ z: 1, row: 1, col: 1, key: "1_1_1" });
    } else if (level >= 2) {
      // 从2级开始使用瓦片图
      // 对于极地投影，使用 n×n 布局
      const tileCount = Math.pow(2, level - 1); // n = 2^(level-1)

      // 计算视窗范围内的瓦片
      const viewportTiles = this.getViewportTiles(level, tileCount);
      tiles.push(...viewportTiles);
    }

    return tiles;
  }

  // 计算视窗范围内的瓦片
  private getViewportTiles(level: number, tileCount: number): { z: number; row: number; col: number; key: string }[] {
    const tiles: { z: number; row: number; col: number; key: string }[] = [];

    // 获取画布尺寸
    const canvasWidth = this.canvas.width;
    const canvasHeight = this.canvas.height;
    const radius = Math.min(canvasWidth, canvasHeight) / 2 - 20;

    // 计算当前视窗在地图坐标系中的边界
    // 注意：这里需要使用与渲染时相同的坐标系

    // 渲染时的变换顺序：translate(width/2 + panX, height/2 + panY) -> scale(scale)
    // 地图坐标 -> 画布坐标：cx = mx * scale + (width/2 + panX), cy = my * scale + (height/2 + panY)
    // 画布坐标 -> 地图坐标：mx = (cx - width/2 - panX) / scale, my = (cy - height/2 - panY) / scale

    // 画布的可见区域 [0, width] x [0, height] 在地图坐标系中的边界
    const mapViewportLeft = (0 - canvasWidth / 2 - this.panX) / this.scale;
    const mapViewportRight = (canvasWidth - canvasWidth / 2 - this.panX) / this.scale;
    const mapViewportTop = (0 - canvasHeight / 2 - this.panY) / this.scale;
    const mapViewportBottom = (canvasHeight - canvasHeight / 2 - this.panY) / this.scale;

    // 计算每个瓦片的尺寸（与渲染时保持一致）
    const tileWidth = (radius * 2) / tileCount;
    const tileHeight = (radius * 2) / tileCount;

    // 计算瓦片在地图坐标系中的范围
    // 瓦片范围是从 -radius 到 +radius
    // 瓦片 col 的 X 坐标范围：[-radius + (col-1)*tileWidth, -radius + col*tileWidth)
    // 瓦片 row 的 Y 坐标范围：[-radius + (row-1)*tileHeight, -radius + row*tileHeight)

    // 计算需要加载的瓦片范围
    // startCol: 第一个与视窗相交的瓦片（瓦片右边界 > 视窗左边界）
    // endCol: 最后一个与视窗相交的瓦片（瓦片左边界 < 视窗右边界）
    const startCol = Math.max(1, Math.floor((mapViewportLeft + radius) / tileWidth) + 1);
    const endCol = Math.min(tileCount, Math.ceil((mapViewportRight + radius) / tileWidth));
    const startRow = Math.max(1, Math.floor((mapViewportTop + radius) / tileHeight) + 1);
    const endRow = Math.min(tileCount, Math.ceil((mapViewportBottom + radius) / tileHeight));

    // 生成视窗范围内的瓦片
    for (let row = startRow; row <= endRow; row++) {
      for (let col = startCol; col <= endCol; col++) {
        tiles.push({ z: level, row, col, key: `${level}_${row}_${col}` });
      }
    }

    return tiles;
  }

  // 变换状态
  private scale = 1;
  private panX = 0;
  private panY = 0;
  private isDragging = false;
  private dragOffset: { x: number; y: number } | null = null;
  private currentZoomLevel = 0; // 初始值设为0，表示未加载

  // 防抖机制
  private tileLoadDebounceTimer: number | null = null;

  // 数据
  private locationPoints: LocationPoint[] = [];
  private detectionPoints: DetectionPointWithApiFields[] = [];
  private dataAreas: LatLngRect[] = [];
  private patrolAreas: LatLngRect[] = []; // 新增巡视区数据

  // 悬停状态
  private hoveredLocation: LocationPoint | null = null;
  private hoveredDetectionPoint: DetectionPointWithApiFields | null = null;
  private hoveredRect: LatLngRect | null = null;
  private hoveredPatrolArea: LatLngRect | null = null;

  // 选区状态
  private selection: SelectionState = {
    isSelecting: false,
    clickCount: 0,
    start: null,
    end: null,
  };

  // 外部选区
  private externalSelection: LatLngRect | null = null;

  // 极区类型
  private polarType: "north" | "south";

  // 模拟Meta键
  private simulatedMetaKey = false;

  // 事件回调
  private callbacks: EarthPolarRendererCallbacks;

  // 事件绑定状态
  private eventsBound = false;

  // 保存事件处理器引用，用于正确移除事件监听器
  private resizeHandler: (() => void) | null = null;

  constructor(canvas: HTMLCanvasElement, polarType: "north" | "south", callbacks: EarthPolarRendererCallbacks = {}) {
    this.canvas = canvas;
    this.polarType = polarType;
    this.callbacks = callbacks;
    this.tileUrlTemplate = callbacks.tileUrlTemplate;
    this.maxZoom = callbacks.maxZoom ?? 5;

    const ctx = canvas.getContext("2d");
    if (!ctx) {
      throw new Error("无法获取Canvas 2D上下文");
    }
    this.ctx = ctx;

    this.init();
  }

  private init() {
    this.loadImage();
    this.setupCanvas();
    this.bindEvents();
  }

  private loadImage() {
    // 如果有tileUrlTemplate，使用tile方式加载
    if (this.tileUrlTemplate) {
      this.loadTiles();
    } else {
      // 否则使用静态图片
      const img = new Image();
      img.onload = () => {
        this.image = img;
        this.imageLoaded = true;
        this.render();
      };
      // 根据极区类型选择不同的图片
      img.src = this.polarType === "south" ? "/test/jidi2.png" : "/test/jidi.png";
    }
  }

  // 防抖的瓦片加载方法
  private loadTilesDebounced() {
    // 清除之前的定时器
    if (this.tileLoadDebounceTimer) {
      clearTimeout(this.tileLoadDebounceTimer);
    }

    // 设置新的定时器，延迟100ms执行
    this.tileLoadDebounceTimer = window.setTimeout(() => {
      this.loadTiles();
    }, 100);
  }

  // 加载视窗瓦片（缩放级别不变时）
  private loadViewportTiles(level: number) {
    // 获取当前视窗范围内的瓦片
    const visibleTiles = this.getVisibleTiles(level);

    // 找出需要加载的新瓦片（缓存中不存在的）
    const newTiles = visibleTiles.filter((tile) => !this.tileCache.has(tile.key));

    if (newTiles.length === 0) {
      return;
    }

    // 加载新瓦片
    const loadPromises = newTiles.map((tile) => this.loadTile(tile.z, tile.row, tile.col));

    Promise.all(loadPromises)
      .then(() => {
        this.render();
      })
      .catch((error) => {
        console.error("视窗瓦片加载失败:", error);
      });
  }

  private loadTiles() {
    // 计算当前缩放级别
    const newZoomLevel = this.getZoomLevel();

    // 如果是初始加载（currentZoomLevel为0），强制加载level 1
    if (this.currentZoomLevel === 0) {
      this.currentZoomLevel = 1;
      this.loadTile(1, 1, 1)
        .then(() => {
          this.imageLoaded = true;
          this.render();
        })
        .catch((error) => {
          console.error("初始瓦片加载失败:", error);
          this.loadStaticImage();
        });
      return;
    }

    // 如果缩放级别没有变化，检查是否需要加载新的视窗瓦片
    if (newZoomLevel === this.currentZoomLevel) {
      this.loadViewportTiles(newZoomLevel);
      return;
    }

    this.currentZoomLevel = newZoomLevel;

    // 获取可见瓦片
    const visibleTiles = this.getVisibleTiles(newZoomLevel);

    // 加载所有可见瓦片
    const loadPromises = visibleTiles.map((tile) => this.loadTile(tile.z, tile.row, tile.col));

    Promise.all(loadPromises)
      .then(() => {
        this.imageLoaded = true;
        this.render();
      })
      .catch((error) => {
        console.error("瓦片加载失败:", error);
        // 如果瓦片加载失败，回退到静态图片
        this.loadStaticImage();
      });
  }

  private loadStaticImage() {
    const img = new Image();
    img.onload = () => {
      this.image = img;
      this.imageLoaded = true;
      this.render();
    };
    img.onerror = () => {
      console.error("静态图片加载也失败了");
    };
    // 根据极区类型选择不同的图片
    img.src = this.polarType === "south" ? "/test/jidi2.png" : "/test/jidi.png";
  }

  private async loadTile(level: number, row: number, col: number): Promise<void> {
    if (!this.tileUrlTemplate) {
      return;
    }

    const tileKey = `${level}_${row}_${col}`;

    // 检查是否已经失败过
    if (this.failedTileCache.has(tileKey)) {
      return;
    }

    // 检查缓存
    if (this.tileCache.has(tileKey)) {
      return;
    }

    // 构建URL
    const tileTag = this.polarType === "north" ? "north" : "south";
    const url = this.tileUrlTemplate
      .replace("{tile_tag}", tileTag)
      .replace("{z}", level.toString())
      .replace("{y}", row.toString())
      .replace("{x}", col.toString());

    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => {
        this.tileCache.set(tileKey, img);
        resolve();
      };
      img.onerror = () => {
        this.failedTileCache.add(tileKey);
        console.warn(`极地瓦片加载失败: ${tileKey}`);
        reject(new Error(`Failed to load tile: ${tileKey}`));
      };
      img.src = url;
    });
  }

  private setupCanvas() {
    const resizeCanvas = () => {
      if (this.canvas.parentElement) {
        const rect = this.canvas.parentElement.getBoundingClientRect();
        this.canvas.width = rect.width;
        this.canvas.height = rect.height;
        requestAnimationFrame(() => {
          this.render();
        });
      }
    };

    resizeCanvas();
    this.resizeHandler = resizeCanvas;
    window.addEventListener("resize", this.resizeHandler);
  }

  private bindEvents() {
    if (this.eventsBound) return;

    this.canvas.addEventListener("wheel", this.handleWheel.bind(this), { passive: false });
    this.canvas.addEventListener("mousedown", this.handleMouseDown.bind(this));
    this.canvas.addEventListener("click", this.handleClick.bind(this));
    this.canvas.addEventListener("mousemove", this.handleMouseMove.bind(this));

    this.eventsBound = true;
  }

  private unbindEvents() {
    if (!this.eventsBound) return;

    // 移除 canvas 事件监听器（注意：由于使用了 bind(this)，这些可能无法完全移除）
    // 但至少尝试移除，减少潜在的内存泄漏
    this.canvas.removeEventListener("wheel", this.handleWheel.bind(this));
    this.canvas.removeEventListener("mousedown", this.handleMouseDown.bind(this));
    this.canvas.removeEventListener("click", this.handleClick.bind(this));
    this.canvas.removeEventListener("mousemove", this.handleMouseMove.bind(this));

    // 移除全局鼠标事件监听器
    window.removeEventListener("mousemove", this.handleGlobalMouseMove.bind(this));
    window.removeEventListener("mouseup", this.handleGlobalMouseUp.bind(this));

    // 移除 resize 事件监听器
    if (this.resizeHandler) {
      window.removeEventListener("resize", this.resizeHandler);
      this.resizeHandler = null;
    }

    this.eventsBound = false;
  }

  // 坐标转换
  private getCoordinateFromScreen(screenX: number, screenY: number) {
    return PolarProjectionUtils.screenToLatLng(
      screenX,
      screenY,
      this.canvas.width,
      this.canvas.height,
      this.panX,
      this.panY,
      this.scale,
      this.polarType
    );
  }

  // 检查点击位置
  private checkPointClick(screenX: number, screenY: number) {
    // 获取当前地图级别
    const currentLevel = this.getZoomLevel();
    
    // 根据地图级别过滤location，只检测level小于等于地图level的location
    const filteredLocations = this.locationPoints.filter((location) => {
      return location.level <= currentLevel;
    });

    // 检查地点
    for (const location of filteredLocations) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        location.center_latitude,
        location.center_longitude,
        this.canvas.width,
        this.canvas.height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );

      if (screenPos) {
        const distance = Math.sqrt(Math.pow(screenX - screenPos.x, 2) + Math.pow(screenY - screenPos.y, 2));
        if (distance <= 20) {
          return { type: "location", data: location };
        }
      }
    }

    // 检查探测点
    for (const detectionPoint of this.detectionPoints) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        detectionPoint.center_latitude,
        detectionPoint.center_longitude,
        this.canvas.width,
        this.canvas.height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );

      if (screenPos) {
        const distance = Math.sqrt(Math.pow(screenX - screenPos.x, 2) + Math.pow(screenY - screenPos.y, 2));
        if (distance <= 10) {
          return { type: "detection", data: detectionPoint };
        }
      }
    }

    // 检查矩形区域
    for (const rect of this.dataAreas) {
      if (
        PolarProjectionUtils.isInPolarRange(rect.minLat, rect.minLng, this.polarType) ||
        PolarProjectionUtils.isInPolarRange(rect.maxLat, rect.maxLng, this.polarType)
      ) {
        const topLeft = PolarProjectionUtils.latLngToScreen(
          rect.maxLat,
          rect.minLng,
          this.canvas.width,
          this.canvas.height,
          this.panX,
          this.panY,
          this.scale,
          this.polarType
        );
        const bottomRight = PolarProjectionUtils.latLngToScreen(
          rect.minLat,
          rect.maxLng,
          this.canvas.width,
          this.canvas.height,
          this.panX,
          this.panY,
          this.scale,
          this.polarType
        );

        if (topLeft && bottomRight) {
          const minX = Math.min(topLeft.x, bottomRight.x);
          const maxX = Math.max(topLeft.x, bottomRight.x);
          const minY = Math.min(topLeft.y, bottomRight.y);
          const maxY = Math.max(topLeft.y, bottomRight.y);

          if (screenX >= minX && screenX <= maxX && screenY >= minY && screenY <= maxY) {
            return { type: "rect", data: rect };
          }
        }
      }
    }

    // 检查巡视区
    const coord = this.getCoordinateFromScreen(screenX, screenY);

    if (coord) {
      for (const patrolArea of this.patrolAreas) {
        // 归一化坐标
        const normalizedLat = coord.lat;
        const normalizedLng = ((((coord.lng + 180) % 360) + 360) % 360) - 180;

        // 归一化巡视区边界
        const normalizedMinLng = ((((patrolArea.minLng + 180) % 360) + 360) % 360) - 180;
        const normalizedMaxLng = ((((patrolArea.maxLng + 180) % 360) + 360) % 360) - 180;

        // 检查点击的经纬度是否在巡视区范围内
        if (normalizedLat >= patrolArea.minLat && normalizedLat <= patrolArea.maxLat) {
          // 处理经度范围，考虑跨越180度边界的情况
          let inLngRange = false;
          if (normalizedMinLng <= normalizedMaxLng) {
            // 正常情况，不跨越180度
            inLngRange = normalizedLng >= normalizedMinLng && normalizedLng <= normalizedMaxLng;
          } else {
            // 跨越180度边界的情况
            inLngRange = normalizedLng >= normalizedMinLng || normalizedLng <= normalizedMaxLng;
          }

          if (inLngRange) {
            return { type: "patrol", data: patrolArea };
          }
        }
      }
    }

    return null;
  }

  // 检查悬停位置
  private checkPointHover(screenX: number, screenY: number) {
    // 获取当前地图级别
    const currentLevel = this.getZoomLevel();
    
    // 根据地图级别过滤location，只检测level小于等于地图level的location
    const filteredLocations = this.locationPoints.filter((location) => {
      return location.level <= currentLevel;
    });

    // 检查地点
    for (const location of filteredLocations) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        location.center_latitude,
        location.center_longitude,
        this.canvas.width,
        this.canvas.height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );

      if (screenPos) {
        const distance = Math.sqrt(Math.pow(screenX - screenPos.x, 2) + Math.pow(screenY - screenPos.y, 2));
        if (distance <= 8) {
          return { type: "location", data: location };
        }
      }
    }

    // 检查探测点
    for (const detectionPoint of this.detectionPoints) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        detectionPoint.center_latitude,
        detectionPoint.center_longitude,
        this.canvas.width,
        this.canvas.height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );

      if (screenPos) {
        const distance = Math.sqrt(Math.pow(screenX - screenPos.x, 2) + Math.pow(screenY - screenPos.y, 2));
        if (distance <= 8) {
          return { type: "detection", data: detectionPoint };
        }
      }
    }

    // 检查矩形区域
    for (const rect of this.dataAreas) {
      if (
        PolarProjectionUtils.isInPolarRange(rect.minLat, rect.minLng, this.polarType) ||
        PolarProjectionUtils.isInPolarRange(rect.maxLat, rect.maxLng, this.polarType)
      ) {
        const topLeft = PolarProjectionUtils.latLngToScreen(
          rect.maxLat,
          rect.minLng,
          this.canvas.width,
          this.canvas.height,
          this.panX,
          this.panY,
          this.scale,
          this.polarType
        );
        const bottomRight = PolarProjectionUtils.latLngToScreen(
          rect.minLat,
          rect.maxLng,
          this.canvas.width,
          this.canvas.height,
          this.panX,
          this.panY,
          this.scale,
          this.polarType
        );

        if (topLeft && bottomRight) {
          const minX = Math.min(topLeft.x, bottomRight.x);
          const maxX = Math.max(topLeft.x, bottomRight.x);
          const minY = Math.min(topLeft.y, bottomRight.y);
          const maxY = Math.max(topLeft.y, bottomRight.y);

          if (screenX >= minX && screenX <= maxX && screenY >= minY && screenY <= maxY) {
            return { type: "rect", data: rect };
          }
        }
      }
    }

    // 检查巡视区
    const coord = this.getCoordinateFromScreen(screenX, screenY);
    if (coord) {
      for (const patrolArea of this.patrolAreas) {
        // 归一化坐标
        const normalizedLat = coord.lat;
        const normalizedLng = ((((coord.lng + 180) % 360) + 360) % 360) - 180;

        // 归一化巡视区边界
        const normalizedMinLng = ((((patrolArea.minLng + 180) % 360) + 360) % 360) - 180;
        const normalizedMaxLng = ((((patrolArea.maxLng + 180) % 360) + 360) % 360) - 180;

        // 检查悬停的经纬度是否在巡视区范围内
        if (normalizedLat >= patrolArea.minLat && normalizedLat <= patrolArea.maxLat) {
          // 处理经度范围，考虑跨越180度边界的情况
          let inLngRange = false;
          if (normalizedMinLng <= normalizedMaxLng) {
            // 正常情况，不跨越180度
            inLngRange = normalizedLng >= normalizedMinLng && normalizedLng <= normalizedMaxLng;
          } else {
            // 跨越180度边界的情况
            inLngRange = normalizedLng >= normalizedMinLng || normalizedLng <= normalizedMaxLng;
          }

          if (inLngRange) {
            return { type: "patrol", data: patrolArea };
          }
        }
      }
    }

    return null;
  }

  // 绘制矩形区域
  private drawRect(
    rect: LatLngRect,
    style: {
      strokeStyle: string;
      fillStyle: string;
      lineWidth: number;
      textColor?: string; // 添加字体颜色参数
    },
    showName: boolean = true
  ) {
    // 检查矩形是否在极区范围内
    const inRange1 = PolarProjectionUtils.isInPolarRange(rect.minLat, rect.minLng, this.polarType);
    const inRange2 = PolarProjectionUtils.isInPolarRange(rect.maxLat, rect.maxLng, this.polarType);

    if (!inRange1 && !inRange2) {
      return;
    }

    const width = this.canvas.width;
    const height = this.canvas.height;

    // 生成矩形的边界点，沿着经纬度线
    const points: { x: number; y: number }[] = [];

    // 1. 上边（最大纬度线）
    const topLat = rect.maxLat;
    const lngStep = Math.max((rect.maxLng - rect.minLng) / 20, 0.001);
    for (let lng = rect.minLng; lng <= rect.maxLng; lng += lngStep) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        this.polarType === "north" ? Math.min(topLat, 90) : Math.min(topLat, -60),
        lng,
        width,
        height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );
      if (screenPos) {
        points.push(screenPos);
      }
    }

    // 2. 右边（最大经度线）
    const rightLng = rect.maxLng;
    const latStep = Math.max((rect.maxLat - rect.minLat) / 20, 0.001);
    for (let lat = rect.maxLat; lat >= rect.minLat; lat -= latStep) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        lat,
        rightLng,
        width,
        height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );
      if (screenPos) {
        points.push(screenPos);
      }
    }

    // 3. 下边（最小纬度线）
    const bottomLat = rect.minLat;
    for (let lng = rect.maxLng; lng >= rect.minLng; lng -= lngStep) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        this.polarType === "north" ? Math.max(bottomLat, 60) : Math.max(bottomLat, -90),
        lng,
        width,
        height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );
      if (screenPos) {
        points.push(screenPos);
      }
    }

    // 4. 左边（最小经度线）
    const leftLng = rect.minLng;
    for (let lat = rect.minLat; lat <= rect.maxLat; lat += latStep) {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        lat,
        leftLng,
        width,
        height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );
      if (screenPos) {
        points.push(screenPos);
      }
    }

    // 绘制路径
    if (points.length > 0) {
      this.ctx.strokeStyle = style.strokeStyle;
      this.ctx.fillStyle = style.fillStyle;
      this.ctx.lineWidth = style.lineWidth;

      this.ctx.beginPath();
      this.ctx.moveTo(points[0].x, points[0].y);
      for (let i = 1; i < points.length; i++) {
        this.ctx.lineTo(points[i].x, points[i].y);
      }
      this.ctx.closePath();

      this.ctx.fill();
      this.ctx.stroke();

      // 绘制矩形名称
      if (showName && rect.name && this.scale > 0.5) {
        // 先处理边界，将矩形限制在极地范围内
        let adjustedMinLat = rect.minLat;
        let adjustedMaxLat = rect.maxLat;

        if (this.polarType === "north") {
          // 北极：限制在60°-90°范围内
          adjustedMinLat = Math.max(60, rect.minLat);
          adjustedMaxLat = Math.min(90, rect.maxLat);
        } else {
          // 南极：限制在-90°到-60°范围内
          adjustedMinLat = Math.max(-90, rect.minLat);
          adjustedMaxLat = Math.min(-60, rect.maxLat);
        }

        // 处理完边界后再取中间值
        const centerLat = (adjustedMinLat + adjustedMaxLat) / 2;
        const centerLng = (rect.minLng + rect.maxLng) / 2;

        // 将调整后的中间经纬度转换为屏幕坐标
        const screenPos = PolarProjectionUtils.latLngToScreen(
          centerLat,
          centerLng,
          this.canvas.width,
          this.canvas.height,
          this.panX,
          this.panY,
          this.scale,
          this.polarType
        );

        if (screenPos) {
          this.ctx.fillStyle = "#333";
          // 字体大小保持固定，不随地图缩放变化
          this.ctx.font = `12px Arial`;
          this.ctx.textAlign = "center";
          this.ctx.textBaseline = "middle";

          // 绘制文字阴影
          this.ctx.fillStyle = "#000";
          this.ctx.fillText(rect.name, screenPos.x + 1, screenPos.y + 1);

          // 绘制文字
          this.ctx.fillStyle = style.textColor || "#fff";
          this.ctx.fillText(rect.name, screenPos.x, screenPos.y);
        }
      }
    }
  }

  // 渲染
  public render() {
    if (!this.imageLoaded) {
      return;
    }

    const width = this.canvas.width;
    const height = this.canvas.height;

    // 清空画布
    this.ctx.clearRect(0, 0, width, height);

    // 保存画布状态
    this.ctx.save();

    // 应用变换
    this.ctx.translate(width / 2 + this.panX, height / 2 + this.panY);
    this.ctx.scale(this.scale, this.scale);

    const radius = Math.min(width, height) / 2 - 20;

    // 绘制圆形底图
    this.ctx.beginPath();
    this.ctx.arc(0, 0, radius, 0, 2 * Math.PI);
    this.ctx.clip();

    // 绘制图片或瓦片
    if (this.tileUrlTemplate) {
      // 使用瓦片模式：先绘制底图，再绘制当前级别的瓦片

      // 1. 始终绘制level 1作为底图
      if (this.tileCache.has("1_1_1")) {
        const baseTile = this.tileCache.get("1_1_1");
        if (baseTile) {
          this.ctx.drawImage(baseTile, -radius, -radius, radius * 2, radius * 2);
        }
      }

      // 2. 如果当前级别不是level 1，叠加绘制当前级别的瓦片
      if (this.currentZoomLevel > 1) {
        const visibleTiles = this.getVisibleTiles(this.currentZoomLevel);

        // 检查哪些瓦片缺失
        const missingTiles = visibleTiles.filter((tile) => !this.tileCache.has(tile.key));

        let renderedCount = 0;
        visibleTiles.forEach((tile) => {
          if (this.tileCache.has(tile.key)) {
            const tileImg = this.tileCache.get(tile.key);
            if (tileImg) {
              // 对于极地投影，使用 n×n 布局
              const tileCount = Math.pow(2, tile.z - 1); // n = 2^(level-1)
              const tileWidth = (radius * 2) / tileCount;
              const tileHeight = (radius * 2) / tileCount;
              // row 控制 Y 位置，col 控制 X 位置
              const tileX = -radius + (tile.col - 1) * tileWidth;
              const tileY = -radius + (tile.row - 1) * tileHeight;

              this.ctx.drawImage(tileImg, tileX, tileY, tileWidth, tileHeight);
              renderedCount++;
            }
          } else {
          }
        });
      }
    } else if (this.image) {
      // 使用静态图片

      this.ctx.drawImage(this.image, -radius, -radius, radius * 2, radius * 2);
    }

    // 绘制极区边界
    this.ctx.beginPath();
    this.ctx.arc(0, 0, radius, 0, 2 * Math.PI);
    this.ctx.strokeStyle = "#333";
    this.ctx.lineWidth = 2 / this.scale;
    this.ctx.stroke();

    this.ctx.globalAlpha = 1;

    // 绘制探测点标记
    this.detectionPoints.forEach((detectionPoint) => {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        detectionPoint.center_latitude,
        detectionPoint.center_longitude,
        width,
        height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );

      if (screenPos) {
        if (
          PolarProjectionUtils.isInPolarRange(
            detectionPoint.center_latitude,
            detectionPoint.center_longitude,
            this.polarType
          )
        ) {
          this.ctx.beginPath();
          this.ctx.arc(screenPos.x, screenPos.y, 5 / this.scale, 0, 2 * Math.PI);
          this.ctx.fillStyle = this.hoveredDetectionPoint === detectionPoint ? "#ff9f43" : "#feca57";
          this.ctx.fill();
          this.ctx.strokeStyle = "#fff";
          this.ctx.lineWidth = 2 / this.scale;
          this.ctx.stroke();

          if (this.scale > 0.5) {
            this.ctx.fillStyle = "#333";
            // 字体大小保持固定，不随地图缩放变化
            this.ctx.font = `10px Arial`;
            this.ctx.textAlign = "center";
            this.ctx.textBaseline = "top";
            this.ctx.fillText(detectionPoint.name, screenPos.x, screenPos.y + 8);
          }
        }
      }
    });

    // 恢复画布状态
    this.ctx.restore();

    // 绘制矩形区域
    this.dataAreas.forEach((rect) => {
      // 检查是否与当前选区相交
      let isIntersecting = false;
      if (this.selection.isSelecting && this.selection.start && this.selection.end) {
        const lngRange = this.getPolarLngRange(this.selection.start.lng, this.selection.end.lng);
        const selectionRect = {
          minLat: Math.min(this.selection.start.lat, this.selection.end.lat),
          maxLat: Math.max(this.selection.start.lat, this.selection.end.lat),
          minLng: lngRange.minLng,
          maxLng: lngRange.maxLng,
        };
        isIntersecting = rectIntersects(rect, selectionRect);
      } else if (this.externalSelection) {
        isIntersecting = rectIntersects(rect, this.externalSelection);
      }

      // 根据状态设置样式：相交时红色边框无填充，悬停时红色，正常时绿色
      let style;
      if (isIntersecting) {
        style = {
          strokeStyle: "#ff0000",
          fillStyle: "transparent",
          lineWidth: 1,
        };
      } else {
        style = {
          strokeStyle: "#00ff00",
          fillStyle: "transparent",
          lineWidth: 1,
        };
      }

      this.drawRect(rect, style, true);
    });

    // 绘制巡视区（使用不同颜色）
    this.patrolAreas.forEach((area) => {
      const style = {
        strokeStyle: "#ff6b35", // 橙色
        fillStyle: "transparent",
        lineWidth: 2,
        textColor: "#ff6b35", // 巡视区标签也使用橙色
      };

      this.drawRect(area, style, true);
    });

    // 绘制选区
    if (this.selection.isSelecting && this.selection.start && this.selection.end) {
      const lngRange = this.getPolarLngRange(this.selection.start.lng, this.selection.end.lng);
      const selectionRect = {
        minLat: Math.min(this.selection.start.lat, this.selection.end.lat),
        maxLat: Math.max(this.selection.start.lat, this.selection.end.lat),
        minLng: lngRange.minLng,
        maxLng: lngRange.maxLng,
        name: "选区",
      };

      const style = {
        strokeStyle: "#ffcc00",
        fillStyle: "rgba(255, 204, 0, 0.1)",
        lineWidth: 1,
      };

      this.drawRect(selectionRect, style, false);
    } else if (!this.selection.isSelecting && this.externalSelection) {
      const style = {
        strokeStyle: "#ffcc00",
        fillStyle: "rgba(255, 204, 0, 0.1)",
        lineWidth: 1,
      };

      this.drawRect(this.externalSelection, style, false);
    }

    // 绘制地点标记
    // 获取当前地图级别
    const currentLevel = this.getZoomLevel();

    // 根据地图级别过滤location，只显示level小于等于地图level的location
    const filteredLocations = this.locationPoints.filter((location) => {
      return location.level <= currentLevel;
    });

    filteredLocations.forEach((location) => {
      const screenPos = PolarProjectionUtils.latLngToScreen(
        location.center_latitude,
        location.center_longitude,
        width,
        height,
        this.panX,
        this.panY,
        this.scale,
        this.polarType
      );

      if (screenPos) {
        if (PolarProjectionUtils.isInPolarRange(location.center_latitude, location.center_longitude, this.polarType)) {
          this.ctx.beginPath();
          this.ctx.arc(screenPos.x, screenPos.y, 6, 0, 2 * Math.PI);
          this.ctx.fillStyle = "#4ecdc4";
          this.ctx.fill();
          this.ctx.strokeStyle = "#fff";
          this.ctx.lineWidth = 2;
          this.ctx.stroke();

          if (this.scale > 0.5) {
            this.ctx.font = "12px Arial";
            this.ctx.textAlign = "center";
            this.ctx.textBaseline = "top";

            this.ctx.fillStyle = "#000";
            this.ctx.fillText(location.name, screenPos.x + 1, screenPos.y + 11);

            this.ctx.fillStyle = "#fff";
            this.ctx.fillText(location.name, screenPos.x, screenPos.y + 10);
          }
        }
      }
    });
  }

  // 事件处理
  private handleWheel(e: WheelEvent) {
    e.preventDefault();
    e.stopPropagation();

    const rect = this.canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    const zoomFactor = e.deltaY > 0 ? 0.9 : 1.1;
    let newScale = Math.min(10, this.scale * zoomFactor);

    // 最小缩放倍数为0.5
    const minScale = 0.5;
    newScale = Math.max(newScale, minScale);

    const scaleRatio = newScale / this.scale;
    const canvasCenterX = rect.width / 2;
    const canvasCenterY = rect.height / 2;
    const mouseOffsetX = x - canvasCenterX;
    const mouseOffsetY = y - canvasCenterY;

    const newPanX = this.panX - mouseOffsetX * (scaleRatio - 1);
    const newPanY = this.panY - mouseOffsetY * (scaleRatio - 1);

    const maxRadius = Math.min(rect.width, rect.height) / 2 - 20;
    const maxPanX = maxRadius;
    const minPanX = -maxRadius;
    const maxPanY = maxRadius;
    const minPanY = -maxRadius;

    this.panX = Math.max(minPanX, Math.min(maxPanX, newPanX));
    this.panY = Math.max(minPanY, Math.min(maxPanY, newPanY));
    this.scale = newScale;

    // 先立即渲染当前状态，然后检查是否需要加载新的瓦片
    this.render();
    
    // 如果使用瓦片，检查是否需要加载新的瓦片级别
    if (this.tileUrlTemplate) {
      this.loadTiles();
    }
  }

  private handleMouseDown(e: MouseEvent) {
    const rect = this.canvas.getBoundingClientRect();
    if (!rect) return;

    if (e.metaKey || this.simulatedMetaKey) {
      return;
    }

    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    const canvasCenterX = rect.width / 2;
    const canvasCenterY = rect.height / 2;
    const offsetX = x - canvasCenterX - this.panX;
    const offsetY = y - canvasCenterY - this.panY;

    this.isDragging = true;
    this.dragOffset = { x: offsetX, y: offsetY };

    window.addEventListener("mousemove", this.handleGlobalMouseMove.bind(this));
    window.addEventListener("mouseup", this.handleGlobalMouseUp.bind(this));
  }

  private handleGlobalMouseMove(e: MouseEvent) {
    const rect = this.canvas.getBoundingClientRect();
    if (!rect) return;

    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    if (this.isDragging && this.dragOffset) {
      const canvasCenterX = rect.width / 2;
      const canvasCenterY = rect.height / 2;
      const newPanX = x - canvasCenterX - this.dragOffset.x;
      const newPanY = y - canvasCenterY - this.dragOffset.y;

      this.panX = newPanX;
      this.panY = newPanY;

      // 先立即渲染当前状态
      this.render();
      
      // 如果使用瓦片，检查是否需要加载新的瓦片
      if (this.tileUrlTemplate) {
        this.loadTilesDebounced();
      }
    }
  }

  private handleGlobalMouseUp() {
    this.isDragging = false;
    this.dragOffset = null;

    window.removeEventListener("mousemove", this.handleGlobalMouseMove.bind(this));
    window.removeEventListener("mouseup", this.handleGlobalMouseUp.bind(this));
  }

  private handleClick(e: MouseEvent) {
    if (this.isDragging) return;

    const rect = this.canvas.getBoundingClientRect();
    if (!rect) return;

    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;

    const selectionMode = e.metaKey || this.simulatedMetaKey;

    if (selectionMode) {
      const coord = this.getCoordinateFromScreen(screenX, screenY);
      if (coord) {
        if (this.selection.clickCount === 0) {
          // 第一次点击
          this.selection.start = coord;
          this.selection.end = coord;
          this.selection.clickCount = 1;
          this.selection.isSelecting = true;
        } else if (this.selection.clickCount === 1) {
          // 第二次点击
          this.selection.end = coord;
          this.selection.clickCount = 0;

          if (this.selection.start) {
            const lngRange = this.getPolarLngRange(this.selection.start.lng, coord.lng);
            const finalSelectionRect: LatLngRect = {
              minLat: Math.min(this.selection.start.lat, coord.lat),
              maxLat: Math.max(this.selection.start.lat, coord.lat),
              minLng: lngRange.minLng,
              maxLng: lngRange.maxLng,
            };

            const intersectingRects = this.dataAreas.filter((rect) => rectIntersects(rect, finalSelectionRect));
            const intersectingDetectionPoints = this.detectionPoints.filter((point) => {
              return pointIntersectsRect(
                { lat: point.center_latitude, lng: point.center_longitude },
                finalSelectionRect
              );
            });

            if (this.callbacks.onSelectionEnd) {
              this.callbacks.onSelectionEnd(finalSelectionRect, intersectingRects, intersectingDetectionPoints);
            }
          }

          this.selection.isSelecting = false;
          this.selection.start = null;
          this.selection.end = null;
        }
      }
      this.render();
      return;
    }

    // 检查点击位置
    const clickedPoint = this.checkPointClick(screenX, screenY);
    if (clickedPoint) {
      if (clickedPoint.type === "location" && this.callbacks.onLocationClick) {
        this.callbacks.onLocationClick(clickedPoint.data as LocationPoint);
      } else if (clickedPoint.type === "detection" && this.callbacks.onDetectionPointClick) {
        this.callbacks.onDetectionPointClick(clickedPoint.data as DetectionPointWithApiFields);
      } else if (clickedPoint.type === "rect" && this.callbacks.onRectClick) {
        this.callbacks.onRectClick(clickedPoint.data as LatLngRect);
      } else if (clickedPoint.type === "patrol" && this.callbacks.onPatrolAreaClick) {
        this.callbacks.onPatrolAreaClick(clickedPoint.data as LatLngRect);
      }
      return;
    }

    // 处理坐标点击
    const coord = this.getCoordinateFromScreen(screenX, screenY);
    if (this.callbacks.onCoordinateClick) {
      this.callbacks.onCoordinateClick(coord);
    }
  }

  private handleMouseMove(e: MouseEvent) {
    const rect = this.canvas.getBoundingClientRect();
    if (!rect) return;

    const screenX = e.clientX - rect.left;
    const screenY = e.clientY - rect.top;

    // 选区模式下，实时更新选区预览
    if (this.selection.isSelecting && this.selection.start) {
      const coord = this.getCoordinateFromScreen(screenX, screenY);
      if (coord) {
        this.selection.end = coord;

        if (this.callbacks.onSelectionChange) {
          const lngRange = this.getPolarLngRange(this.selection.start.lng, coord.lng);
          const selectionRect: LatLngRect = {
            minLat: Math.min(this.selection.start.lat, coord.lat),
            maxLat: Math.max(this.selection.start.lat, coord.lat),
            minLng: lngRange.minLng,
            maxLng: lngRange.maxLng,
          };
          this.callbacks.onSelectionChange(selectionRect);
        }

        this.render();
      }
      return;
    }

    // 检查悬停位置
    const hoveredPoint = this.checkPointHover(screenX, screenY);
    if (hoveredPoint) {
      if (hoveredPoint.type === "location") {
        const location = hoveredPoint.data as LocationPoint;
        if (this.hoveredLocation !== location) {
          this.hoveredLocation = location;
          this.hoveredDetectionPoint = null;
          this.hoveredRect = null;
          if (this.callbacks.onLocationHover) {
            this.callbacks.onLocationHover(location);
          }
          this.render();
        }
      } else if (hoveredPoint.type === "detection") {
        const detectionPoint = hoveredPoint.data as DetectionPointWithApiFields;
        if (this.hoveredDetectionPoint !== detectionPoint) {
          this.hoveredDetectionPoint = detectionPoint;
          this.hoveredLocation = null;
          this.hoveredRect = null;
          if (this.callbacks.onDetectionPointHover) {
            this.callbacks.onDetectionPointHover(detectionPoint);
          }
          this.render();
        }
      } else if (hoveredPoint.type === "rect") {
        const rect = hoveredPoint.data as LatLngRect;
        if (this.hoveredRect !== rect) {
          this.hoveredRect = rect;
          this.hoveredLocation = null;
          this.hoveredDetectionPoint = null;
          this.hoveredPatrolArea = null;
          if (this.callbacks.onRectHover) {
            this.callbacks.onRectHover(rect);
          }
          this.render();
        }
      } else if (hoveredPoint.type === "patrol") {
        const patrolArea = hoveredPoint.data as LatLngRect;
        if (this.hoveredPatrolArea !== patrolArea) {
          this.hoveredPatrolArea = patrolArea;
          this.hoveredLocation = null;
          this.hoveredDetectionPoint = null;
          this.hoveredRect = null;
          if (this.callbacks.onPatrolAreaHover) {
            this.callbacks.onPatrolAreaHover(patrolArea);
          }
          this.render();
        }
      }
      return;
    }

    // 清除悬停状态
    if (this.hoveredLocation || this.hoveredDetectionPoint || this.hoveredRect || this.hoveredPatrolArea) {
      this.hoveredLocation = null;
      this.hoveredDetectionPoint = null;
      this.hoveredRect = null;
      this.hoveredPatrolArea = null;
      if (this.callbacks.onLocationHover) this.callbacks.onLocationHover(null);
      if (this.callbacks.onDetectionPointHover) this.callbacks.onDetectionPointHover(null);
      if (this.callbacks.onRectHover) this.callbacks.onRectHover(null);
      if (this.callbacks.onPatrolAreaHover) this.callbacks.onPatrolAreaHover(null);
      this.render();
    }

    // 处理坐标悬停
    const coord = this.getCoordinateFromScreen(screenX, screenY);
    if (this.callbacks.onCoordinateHover) {
      this.callbacks.onCoordinateHover(coord);
    }
  }

  // 公共方法
  public setData(
    locationPoints: LocationPoint[],
    detectionPoints: DetectionPointWithApiFields[],
    dataAreas: LatLngRect[],
    patrolAreas: LatLngRect[] = []
  ) {
    this.locationPoints = locationPoints;
    this.detectionPoints = detectionPoints;
    this.dataAreas = dataAreas;
    this.patrolAreas = patrolAreas;
    this.render();
  }

  public setExternalSelection(selection: LatLngRect | null) {
    this.externalSelection = selection;
    this.render();
  }

  public setSimulatedMetaKey(simulated: boolean) {
    this.simulatedMetaKey = simulated;
  }

  public clearSelection() {
    this.selection = {
      isSelecting: false,
      clickCount: 0,
      start: null,
      end: null,
    };
    this.externalSelection = null;
    this.render();
  }

  public zoomIn() {
    this.scale = Math.min(10, this.scale * 1.2);
    
    // 先立即渲染当前状态
    this.render();
    
    // 如果使用瓦片，检查是否需要加载新的瓦片级别
    if (this.tileUrlTemplate) {
      this.loadTiles();
    }
  }

  public zoomOut() {
    this.scale = Math.max(0.5, this.scale / 1.2);
    
    // 先立即渲染当前状态
    this.render();
    
    // 如果使用瓦片，检查是否需要加载新的瓦片级别
    if (this.tileUrlTemplate) {
      this.loadTiles();
    }
  }

  public resetView() {
    this.scale = 1;
    this.panX = 0;
    this.panY = 0;
    
    // 先立即渲染当前状态
    this.render();
    
    // 如果使用瓦片，检查是否需要加载新的瓦片级别
    if (this.tileUrlTemplate) {
      this.loadTiles();
    }
  }

  /**
   * 销毁对象，清理所有资源
   * 建议在组件卸载时调用此方法
   */
  public destroy() {
    // 清理事件监听器
    this.unbindEvents();

    // 清理数据
    this.locationPoints = [];
    this.detectionPoints = [];
    this.dataAreas = [];
    this.patrolAreas = [];

    // 清理状态
    this.hoveredLocation = null;
    this.hoveredDetectionPoint = null;
    this.hoveredRect = null;
    this.hoveredPatrolArea = null;
    this.externalSelection = null;
    this.selection = {
      isSelecting: false,
      clickCount: 0,
      start: null,
      end: null,
    };

    // 清理图片和瓦片资源
    this.image = null;
    this.imageLoaded = false;
    this.tileCache.clear();
    this.failedTileCache.clear();

    // 清理回调
    this.callbacks = {};

    // 重置变换状态
    this.scale = 1;
    this.panX = 0;
    this.panY = 0;
    this.isDragging = false;
    this.dragOffset = null;
  }
}
